<!DOCTYPE html>
<!-- saved from url=(0039)http://mybatis.org/spring/zh/batch.html -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh" lang="zh"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="Eduardo Macarron">
    <meta name="author" content="Brice Dutheil">
    <meta name="Date-Revision-yyyymmdd" content="20200309">
    <meta http-equiv="Content-Language" content="zh">
    <title>mybatis-spring – MyBatis-Spring | 使用 Spring Batch</title>
    <link rel="stylesheet" href="./mybatis-spring – MyBatis-Spring _ 使用 Spring Batch_files/apache-maven-fluido-1.7.min.css">
    <link rel="stylesheet" href="./mybatis-spring – MyBatis-Spring _ 使用 Spring Batch_files/site.css">
    <link rel="stylesheet" href="./mybatis-spring – MyBatis-Spring _ 使用 Spring Batch_files/print.css" media="print">
    <script type="text/javascript" src="./mybatis-spring – MyBatis-Spring _ 使用 Spring Batch_files/apache-maven-fluido-1.7.min.js.下载"></script>
  </head>
  <body class="topBarDisabled">
    <div class="container-fluid">
      <div id="banner">
        <div class="pull-left"><div id="bannerLeft"><h2>mybatis-spring</h2>
</div>
</div>
        <div class="pull-right"><a href="http://mybatis.org/" id="bannerRight" title="MyBatis logo"><img src="./mybatis-spring – MyBatis-Spring _ 使用 Spring Batch_files/mybatis-logo.png" alt="MyBatis logo"></a></div>
        <div class="clear"><hr></div>
      </div>

      <div id="breadcrumbs">
        <ul class="breadcrumb">
        <li id="publishDate">最近更新: 09 三月 2020<span class="divider">|</span>
</li>
          <li id="projectVersion">版本: 2.0.4</li>
        </ul>
      </div>
      <div class="row-fluid">
        <div id="leftColumn" class="span2">
          <div class="well sidebar-nav">
    <ul class="nav nav-list">
      <li class="nav-header">参考文档</li>
    <li><a href="http://mybatis.org/spring/zh/index.html" title="简介"><span class="none"></span>简介</a></li>
    <li><a href="http://mybatis.org/spring/zh/getting-started.html" title="入门"><span class="none"></span>入门</a></li>
    <li><a href="http://mybatis.org/spring/zh/factorybean.html" title="SqlSessionFactoryBean"><span class="none"></span>SqlSessionFactoryBean</a></li>
    <li><a href="http://mybatis.org/spring/zh/transactions.html" title="事务"><span class="icon-chevron-right"></span>事务</a></li>
    <li><a href="http://mybatis.org/spring/zh/sqlsession.html" title="使用 SqlSession"><span class="icon-chevron-right"></span>使用 SqlSession</a></li>
    <li><a href="http://mybatis.org/spring/zh/mappers.html" title="注入映射器"><span class="icon-chevron-right"></span>注入映射器</a></li>
    <li><a href="http://mybatis.org/spring/zh/boot.html" title="使用 Spring Boot"><span class="none"></span>使用 Spring Boot</a></li>
    <li><a href="http://mybatis.org/spring/zh/using-api.html" title="使用 MyBatis API"><span class="none"></span>使用 MyBatis API</a></li>
    <li class="active"><a href="http://mybatis.org/spring/zh/batch.html#"><span class="none"></span>使用 Spring Batch</a></li>
    <li><a href="http://mybatis.org/spring/zh/sample.html" title="示例代码"><span class="none"></span>示例代码</a></li>
      <li class="nav-header">项目文档</li>
    <li><a href="http://mybatis.org/spring/zh/project-info.html" title="项目信息"><span class="icon-chevron-right"></span>项目信息</a></li>
    <li><a href="http://mybatis.org/spring/zh/project-reports.html" title="项目报表"><span class="icon-chevron-right"></span>项目报表</a></li>
</ul>
          <hr>
          <div id="poweredBy">
            <div class="clear"></div>
            <div class="clear"></div>
            <div class="clear"></div>
            <div class="clear"></div>
<a href="http://maven.apache.org/" title="构建依靠 Maven" class="poweredBy"><img class="builtBy" alt="构建依靠 Maven" src="./mybatis-spring – MyBatis-Spring _ 使用 Spring Batch_files/maven-feather.png"></a>
            </div>
          </div>
        </div>
        <div id="bodyColumn" class="span10">


  
    <div class="section">
<h2><a name="a.E4.BD.BF.E7.94.A8_Spring_Batch"></a>使用 Spring Batch</h2>
      
<p>
        MyBatis-Spring 1.1.0 发布以后，提供了三个 bean 以供构建 Spring Batch 应用程序：<tt>MyBatisPagingItemReader</tt>、<tt>MyBatisCursorItemReader</tt> 和 <tt>MyBatisBatchItemWriter</tt>。而在 2.0.0 中，还提供了三个建造器（builder）类来对 Java 配置提供支持：<tt>MyBatisPagingItemReaderBuilder</tt>、<tt>MyBatisCursorItemReaderBuilder</tt> 和 <tt>MyBatisBatchItemWriterBuilder</tt>。
      </p>
      
      
<p>
        <span class="label important">提示</span> 本章是关于 <a class="externalLink" href="http://static.springsource.org/spring-batch/">Spring Batch</a> 的，而不是关于 MyBatis 的批量 SqlSession。要查找关于批量 session 的更多信息，请参考 <a href="http://mybatis.org/spring/zh/sqlsession.html">使用 SqlSession</a> 一章。
      </p>
      
      
<div class="section">
<h3><a name="MyBatisPagingItemReader"></a>MyBatisPagingItemReader</h3>
        
<p>
          这个 bean 是一个 <tt>ItemReader</tt>，能够从数据库中分页地读取记录。
        </p>
        
        
<p>
          它执行 <tt>setQueryId</tt> 属性指定的查询来获取请求的数据。这个查询使用 <tt>setPageSize</tt> 属性指定了分页请求的大小，并被执行。其它的页面将在必要时被请求（例如调用 <tt>read()</tt> 方法时），返回对应位置上的对象。
        </p>
        
        
<p>
          reader 还提供了一些标准的请求参数。在命名查询的 SQL 中，必须使用部分或全部的参数（视乎 SQL 方言而定）来构造指定大小的结果集。这些参数是：
        </p>

        
<ul>
          
<li><tt>_page</tt>： 欲读取的页码（从 0 开始）</li>
          
<li><tt>_pagesize</tt>： 每一页的大小，也就是返回的行数</li>
          
<li><tt>_skiprows</tt>： <tt>_page</tt> 和 <tt>_pagesize</tt> 的乘积</li>
        </ul>

        
<p>它们可以被映射成如下的 select 语句：</p>
        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;select</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"getEmployee"</span><span class="pln"> </span><span class="atn">resultMap</span><span class="pun">=</span><span class="atv">"employeeBatchResult"</span><span class="tag">&gt;</span><span class="pln">
  SELECT id, name, job FROM employees ORDER BY id ASC LIMIT #{_skiprows}, #{_pagesize}
</span><span class="tag">&lt;/select&gt;</span></pre></div>

        
<p>配合如下的配置样例：</p>
        
        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;bean</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"reader"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.batch.MyBatisPagingItemReader"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"sqlSessionFactory"</span><span class="pln"> </span><span class="atn">ref</span><span class="pun">=</span><span class="atv">"sqlSessionFactory"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"queryId"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"com.my.name.space.batch.EmployeeMapper.getEmployee"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/bean&gt;</span></pre></div>

        
<div class="source"><pre class="prettyprint"><span class="lit">@Bean</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">MyBatisPagingItemReader</span><span class="pun">&lt;</span><span class="typ">Employee</span><span class="pun">&gt;</span><span class="pln"> reader</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MyBatisPagingItemReaderBuilder</span><span class="pun">&lt;</span><span class="typ">Employee</span><span class="pun">&gt;()</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">sqlSessionFactory</span><span class="pun">(</span><span class="pln">sqlSessionFactory</span><span class="pun">())</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">queryId</span><span class="pun">(</span><span class="str">"com.my.name.space.batch.EmployeeMapper.getEmployee"</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">build</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre></div>

        
<p><b>让我们通过一个更复杂一点的例子来阐明一切：</b></p>

        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;bean</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"dateBasedCriteriaReader"</span><span class="pln">
  </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.batch.MyBatisPagingItemReader"</span><span class="pln">
  </span><span class="atn">p:sqlSessionFactory-ref</span><span class="pun">=</span><span class="atv">"batchReadingSessionFactory"</span><span class="pln">
  </span><span class="atn">p:parameterValues-ref</span><span class="pun">=</span><span class="atv">"datesParameters"</span><span class="pln">
  </span><span class="atn">p:queryId</span><span class="pun">=</span><span class="atv">"com.my.name.space.batch.ExampleMapper.queryUserInteractionsOnSpecificTimeSlot"</span><span class="pln">
  </span><span class="atn">p:pageSize</span><span class="pun">=</span><span class="atv">"200"</span><span class="pln">
  </span><span class="atn">scope</span><span class="pun">=</span><span class="atv">"step"</span><span class="tag">/&gt;</span><span class="pln">

</span><span class="tag">&lt;util:map</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"datesParameters"</span><span class="pln"> </span><span class="atn">scope</span><span class="pun">=</span><span class="atv">"step"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;entry</span><span class="pln"> </span><span class="atn">key</span><span class="pun">=</span><span class="atv">"yesterday"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"#{jobExecutionContext['EXTRACTION_START_DATE']}"</span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;entry</span><span class="pln"> </span><span class="atn">key</span><span class="pun">=</span><span class="atv">"today"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"#{jobExecutionContext['TODAY_DATE']}"</span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;entry</span><span class="pln"> </span><span class="atn">key</span><span class="pun">=</span><span class="atv">"first_day_of_the_month"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"#{jobExecutionContext['FIRST_DAY_OF_THE_MONTH_DATE']}"</span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;entry</span><span class="pln"> </span><span class="atn">key</span><span class="pun">=</span><span class="atv">"first_day_of_the_previous_month"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"#{jobExecutionContext['FIRST_DAY_OF_THE_PREVIOUS_MONTH_DATE']}"</span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/util:map&gt;</span></pre></div>

        
<div class="source"><pre class="prettyprint"><span class="lit">@StepScope</span><span class="pln">
</span><span class="lit">@Bean</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">MyBatisPagingItemReader</span><span class="pun">&lt;</span><span class="typ">User</span><span class="pun">&gt;</span><span class="pln"> dateBasedCriteriaReader</span><span class="pun">(</span><span class="pln">
    </span><span class="lit">@Value</span><span class="pun">(</span><span class="str">"#{@datesParameters}"</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Map</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">&gt;</span><span class="pln"> datesParameters</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">throws</span><span class="pln"> </span><span class="typ">Exception</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MyBatisPagingItemReaderBuilder</span><span class="pun">&lt;</span><span class="typ">User</span><span class="pun">&gt;()</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">sqlSessionFactory</span><span class="pun">(</span><span class="pln">batchReadingSessionFactory</span><span class="pun">())</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">queryId</span><span class="pun">(</span><span class="str">"com.my.name.space.batch.ExampleMapper.queryUserInteractionsOnSpecificTimeSlot"</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">parameterValues</span><span class="pun">(</span><span class="pln">datesParameters</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">pageSize</span><span class="pun">(</span><span class="lit">200</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">build</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="lit">@StepScope</span><span class="pln">
</span><span class="lit">@Bean</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Map</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">&gt;</span><span class="pln"> datesParameters</span><span class="pun">(</span><span class="pln">
    </span><span class="lit">@Value</span><span class="pun">(</span><span class="str">"#{jobExecutionContext['EXTRACTION_START_DATE']}"</span><span class="pun">)</span><span class="pln"> </span><span class="typ">LocalDate</span><span class="pln"> yesterday</span><span class="pun">,</span><span class="pln">
    </span><span class="lit">@Value</span><span class="pun">(</span><span class="str">"#{jobExecutionContext['TODAY_DATE']}"</span><span class="pun">)</span><span class="pln"> </span><span class="typ">LocalDate</span><span class="pln"> today</span><span class="pun">,</span><span class="pln">
    </span><span class="lit">@Value</span><span class="pun">(</span><span class="str">"#{jobExecutionContext['FIRST_DAY_OF_THE_MONTH_DATE']}"</span><span class="pun">)</span><span class="pln"> </span><span class="typ">LocalDate</span><span class="pln"> firstDayOfTheMonth</span><span class="pun">,</span><span class="pln">
    </span><span class="lit">@Value</span><span class="pun">(</span><span class="str">"#{jobExecutionContext['FIRST_DAY_OF_THE_PREVIOUS_MONTH_DATE']}"</span><span class="pun">)</span><span class="pln"> </span><span class="typ">LocalDate</span><span class="pln"> firstDayOfThePreviousMonth</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Map</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">&gt;</span><span class="pln"> map </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HashMap</span><span class="pun">&lt;&gt;();</span><span class="pln">
  map</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="str">"yesterday"</span><span class="pun">,</span><span class="pln"> yesterday</span><span class="pun">);</span><span class="pln">
  map</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="str">"today"</span><span class="pun">,</span><span class="pln"> today</span><span class="pun">);</span><span class="pln">
  map</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="str">"first_day_of_the_month"</span><span class="pun">,</span><span class="pln"> firstDayOfTheMonth</span><span class="pun">);</span><span class="pln">
  map</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="str">"first_day_of_the_previous_month"</span><span class="pun">,</span><span class="pln"> firstDayOfThePreviousMonth</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> map</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre></div>

        
<p>
          上面的样例使用了几个东西：
        </p>
        
        
<ul>
          
<li><tt>sqlSessionFactory</tt>：可以为 reader 指定你自定义的 sessionFactory，当你想从多个数据库中读取数据时尤其有用</li>
          
<li><tt>queryId</tt>：指定在检索记录时要执行的查询的 ID，可以指定短的 ID 或是带命名空间的完整 ID。一般来说，你的应用可能从多个表或数据库中读取数据，因此会配置多个查询，可能会使用到在不同命名空间中的不同映射器。因此最好提供映射器文件的命名空间以便准确指定你想使用的查询 ID。</li>
          
<li><tt>parameterValues</tt>：可以通过这个 map 传递多个附加的参数，上面的例子中就使用了一个由 Spring 构建的 map，并使用 SpEL 表达式从 <tt>jobExecutionContext</tt> 中获取信息。而 map 的键将在映射器文件中被 MyBatis 使用（例如：<i>yesterday</i> 可以通过 <tt>#{yesterday,jdbcType=TIMESTAMP}</tt> 来读取）。注意，map 和 reader 都构建于 <tt>step</tt> 作用域，这样才能够在 Spring 表达式语言中使用 <tt>jobExecutionContext</tt>。另外，如果正确配置了 MyBatis 的类型处理器，你可以将自定义的实例像参数那样传递到 map 中，比如将参数类型换成 JodaTime。</li>
          
<li><tt>pageSize</tt>：如果批处理流配置了块大小（chunk size），需要通过此属性将块大小告知 reader。</li>
        </ul>

      </div>

      
<div class="section">
<h3><a name="MyBatisCursorItemReader"></a>MyBatisCursorItemReader</h3>
        
<p>
          这个 bean 是一个 <tt>ItemReader</tt> ，能够通过游标从数据库中读取记录。
        </p>

        
<p>
          <span class="label important">提示</span> 为了使用这个 bean，你需要使用 MyBatis 3.4.0 或更新的版本。
        </p>
        
        
<p>
          它执行 <tt>setQueryId</tt> 属性指定的查询来获取请求的数据（通过 <tt>selectCursor()</tt>方法）。每次调用 <tt>read()</tt> 方法时，将会返回游标指向的下个元素，直到没有剩余的元素了。
        </p>
        
        
<p>
          这个 reader 将会使用一个单独的数据库连接，因此 select 语句将不会参与到 step 处理中创建的任何事务。
        </p>

        
<p>当使用游标时，只需要执行普通的查询：</p>
        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;select</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"getEmployee"</span><span class="pln"> </span><span class="atn">resultMap</span><span class="pun">=</span><span class="atv">"employeeBatchResult"</span><span class="tag">&gt;</span><span class="pln">
  SELECT id, name, job FROM employees ORDER BY id ASC
</span><span class="tag">&lt;/select&gt;</span></pre></div>

        
<p>搭配以下的配置样例：</p>
        
        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;bean</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"reader"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.batch.MyBatisCursorItemReader"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"sqlSessionFactory"</span><span class="pln"> </span><span class="atn">ref</span><span class="pun">=</span><span class="atv">"sqlSessionFactory"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"queryId"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"com.my.name.space.batch.EmployeeMapper.getEmployee"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/bean&gt;</span></pre></div>

        
<div class="source"><pre class="prettyprint"><span class="lit">@Bean</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">MyBatisCursorItemReader</span><span class="pun">&lt;</span><span class="typ">Employee</span><span class="pun">&gt;</span><span class="pln"> reader</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MyBatisCursorItemReaderBuilder</span><span class="pun">&lt;</span><span class="typ">Employee</span><span class="pun">&gt;()</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">sqlSessionFactory</span><span class="pun">(</span><span class="pln">sqlSessionFactory</span><span class="pun">())</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">queryId</span><span class="pun">(</span><span class="str">"com.my.name.space.batch.EmployeeMapper.getEmployee"</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">build</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre></div>

      </div>

      
<div class="section">
<h3><a name="MyBatisBatchItemWriter"></a>MyBatisBatchItemWriter</h3>
      
        
<p>
          这是一个 <tt>ItemWriter</tt>，通过利用 <tt>SqlSessionTemplate</tt>
          中的批量处理功能来对给定的所有记录执行多个语句。<tt>SqlSessionFactory</tt>
          需要被配置为 <tt>BATCH</tt> 执行类型。
        </p>

        
<p>
          当调用 <tt>write()</tt> 时，将会执行 <tt>statementId</tt>
          属性中指定的映射语句。一般情况下，<tt>write()</tt>
          应该在一个事务中进行调用。<br>
        </p>
 
        
<p>下面是一个配置样例：</p>
        
        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;bean</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"writer"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.batch.MyBatisBatchItemWriter"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"sqlSessionFactory"</span><span class="pln"> </span><span class="atn">ref</span><span class="pun">=</span><span class="atv">"sqlSessionFactory"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"statementId"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"com.my.name.space.batch.EmployeeMapper.updateEmployee"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/bean&gt;</span></pre></div>

        
<div class="source"><pre class="prettyprint"><span class="lit">@Bean</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">MyBatisBatchItemWriter</span><span class="pun">&lt;</span><span class="typ">User</span><span class="pun">&gt;</span><span class="pln"> writer</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MyBatisBatchItemWriterBuilder</span><span class="pun">&lt;</span><span class="typ">User</span><span class="pun">&gt;()</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">sqlSessionFactory</span><span class="pun">(</span><span class="pln">sqlSessionFactory</span><span class="pun">())</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">statementId</span><span class="pun">(</span><span class="str">"com.my.name.space.batch.EmployeeMapper.updateEmployee"</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">build</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre></div>


        
<p><b>将 ItemReader 读取的记录转换为任意的参数对象：</b></p>

        
<p>
          默认情况下，<tt>MyBatisBatchItemWriter</tt> 会将 <tt>ItemReader</tt>
          读取的对象（或 <tt>ItemProcessor</tt> 转换过的对象) 以参数对象的形式传递给
          MyBatis（<tt>SqlSession#update()</tt>）。
		  如果你想自定义传递给 MyBatis 的参数对象，可以使用 <tt>itemToParameterConverter</tt> 选项。
          使用该选项后，可以传递任意对象给 MyBatis。
          举个例子：
        </p>

        
<p>
		  首先，创建一个自定义的转换器类（或工厂方法）。以下例子使用了工厂方法。
        </p>

        
<div class="source"><pre class="prettyprint"><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">Converter</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Map</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">&gt;&gt;</span><span class="pln"> createItemToParameterMapConverter</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> operationBy</span><span class="pun">,</span><span class="pln"> </span><span class="typ">LocalDateTime</span><span class="pln"> operationAt</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> item </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Map</span><span class="pun">&lt;</span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">&gt;</span><span class="pln"> parameter </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HashMap</span><span class="pun">&lt;&gt;();</span><span class="pln">
    parameter</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="str">"item"</span><span class="pun">,</span><span class="pln"> item</span><span class="pun">);</span><span class="pln">
    parameter</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="str">"operationBy"</span><span class="pun">,</span><span class="pln"> operationBy</span><span class="pun">);</span><span class="pln">
    parameter</span><span class="pun">.</span><span class="pln">put</span><span class="pun">(</span><span class="str">"operationAt"</span><span class="pun">,</span><span class="pln"> operationAt</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> parameter</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre></div>

        
<p>
		  接下来，编写 SQL 映射。
        </p>

        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;select</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"createPerson"</span><span class="pln"> </span><span class="atn">resultType</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.sample.domain.Person"</span><span class="tag">&gt;</span><span class="pln">
    insert into persons (first_name, last_name, operation_by, operation_at)
           values(#{item.firstName}, #{item.lastName}, #{operationBy}, #{operationAt})
</span><span class="tag">&lt;/select&gt;</span></pre></div>

        
<p>
		  最后，配置 <tt>MyBatisBatchItemWriter</tt>。
        </p>

        
<div class="source"><pre class="prettyprint"><span class="com">// Java 配置样例</span><span class="pln">
</span><span class="lit">@Bean</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">MyBatisBatchItemWriter</span><span class="pun">&lt;</span><span class="typ">Person</span><span class="pun">&gt;</span><span class="pln"> writer</span><span class="pun">()</span><span class="pln"> </span><span class="kwd">throws</span><span class="pln"> </span><span class="typ">Exception</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MyBatisBatchItemWriterBuilder</span><span class="pun">&lt;</span><span class="typ">Person</span><span class="pun">&gt;()</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">sqlSessionFactory</span><span class="pun">(</span><span class="pln">sqlSessionFactory</span><span class="pun">())</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">statementId</span><span class="pun">(</span><span class="str">"org.mybatis.spring.sample.mapper.PersonMapper.createPerson"</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">itemToParameterConverter</span><span class="pun">(</span><span class="pln">createItemToParameterMapConverter</span><span class="pun">(</span><span class="str">"batch_java_config_user"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">LocalDateTime</span><span class="pun">.</span><span class="pln">now</span><span class="pun">()))</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">build</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre></div>

        
<div class="source"><pre class="prettyprint"><span class="com">&lt;!-- XML 配置样例 --&gt;</span><span class="pln">
</span><span class="tag">&lt;bean</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"writer"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.batch.MyBatisBatchItemWriter"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"sqlSessionFactory"</span><span class="pln"> </span><span class="atn">ref</span><span class="pun">=</span><span class="atv">"sqlSessionFactory"</span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"statementId"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.sample.mapper.PersonMapper.createPerson"</span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"itemToParameterConverter"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;bean</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.sample.config.SampleJobConfig"</span><span class="pln"> </span><span class="atn">factory-method</span><span class="pun">=</span><span class="atv">"createItemToParameterMapConverter"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;constructor-arg</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"java.lang.String"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"batch_xml_config_user"</span><span class="tag">/&gt;</span><span class="pln">
      </span><span class="tag">&lt;constructor-arg</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"java.time.LocalDateTime"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"#{T(java.time.LocalDateTime).now()}"</span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;/bean&gt;</span><span class="pln">
  </span><span class="tag">&lt;/property&gt;</span><span class="pln">
</span><span class="tag">&lt;/bean&gt;</span></pre></div>


        
<p><b>使用复合 writer 对多个表进行写入（但带有问题）：</b></p>

        
<p>
          这个小技巧只能在 MyBatis 3.2+ 以上的版本中使用，因为之前的版本中含有导致 writer 行为不正确的<a class="externalLink" href="http://code.google.com/p/mybatis/issues/detail?id=741">问题</a>。
        </p>

        
<p>
          如果批量处理时需要写入复杂的数据，例如含有关联的记录，甚至是向多个数据库写入数据，你可能就需要一种办法来绕开 insert 语句只能插入到一个表中的限制。为了绕过此限制，批处理必须准备好要通过 writer 写入的<i>项</i>。然而，基于对被处理的数据的观察，可以尝试使用下面的方法来解决此问题。下面的方法能够工作于具有简单关联或不相关的多个表的项。
        </p>

        
<p>
          在这种方法中，处理 Spring Batch 项的处理器中将会<i>持有</i>各种不同的记录。假设每个项都有一个与 <i>InteractionMetadata</i> 相关联的 <i>Interaction</i>，并且还有两个不相关的行 <i>VisitorInteraction</i> 和
          <i>CustomerInteraction</i>，这时候持有器（holder）看起来像这样：
        </p>

        
<div class="source"><pre class="prettyprint"><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">InteractionRecordToWriteInMultipleTables</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">VisitorInteraction</span><span class="pln"> visitorInteraction</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">CustomerInteraction</span><span class="pln"> customerInteraction</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">Interaction</span><span class="pln"> interaction</span><span class="pun">;</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Interaction</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">final</span><span class="pln"> </span><span class="typ">InteractionMetadata</span><span class="pln"> interactionMetadata</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre></div>

        
<p>
          在 Spring 配置中要配置一个 <tt>CompositeItemWriter</tt>，它将会将写入操作委托到特定种类的 writer 上面去。注意 <i>InteractionMetadata</i> 在例子里面是一个关联，它需要首先被写入，这样 Interaction 才能获得更新之后的键。
        </p>

        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;bean</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"interactionsItemWriter"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"org.springframework.batch.item.support.CompositeItemWriter"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;property</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"delegates"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;list&gt;</span><span class="pln">
      </span><span class="tag">&lt;ref</span><span class="pln"> </span><span class="atn">bean</span><span class="pun">=</span><span class="atv">"visitorInteractionsWriter"</span><span class="tag">/&gt;</span><span class="pln">
      </span><span class="tag">&lt;ref</span><span class="pln"> </span><span class="atn">bean</span><span class="pun">=</span><span class="atv">"customerInteractionsWriter"</span><span class="tag">/&gt;</span><span class="pln">

      </span><span class="com">&lt;!-- 顺序很重要 --&gt;</span><span class="pln">
      </span><span class="tag">&lt;ref</span><span class="pln"> </span><span class="atn">bean</span><span class="pun">=</span><span class="atv">"interactionMetadataWriter"</span><span class="tag">/&gt;</span><span class="pln">
      </span><span class="tag">&lt;ref</span><span class="pln"> </span><span class="atn">bean</span><span class="pun">=</span><span class="atv">"interactionWriter"</span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;/list&gt;</span><span class="pln">
  </span><span class="tag">&lt;/property&gt;</span><span class="pln">
</span><span class="tag">&lt;/bean&gt;</span></pre></div>

        
<div class="source"><pre class="prettyprint"><span class="lit">@Bean</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">CompositeItemWriter</span><span class="pun">&lt;?&gt;</span><span class="pln"> interactionsItemWriter</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">CompositeItemWriter</span><span class="pln"> compositeItemWriter </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CompositeItemWriter</span><span class="pun">();</span><span class="pln">
  </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">ItemWriter</span><span class="pun">&lt;?&gt;&gt;</span><span class="pln"> writers </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;&gt;(</span><span class="lit">4</span><span class="pun">);</span><span class="pln">
  writers</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">visitorInteractionsWriter</span><span class="pun">());</span><span class="pln">
  writers</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">customerInteractionsWriter</span><span class="pun">());</span><span class="pln">
  writers</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">interactionMetadataWriter</span><span class="pun">());</span><span class="pln">
  writers</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">interactionWriter</span><span class="pun">());</span><span class="pln">
  compositeItemWriter</span><span class="pun">.</span><span class="pln">setDelegates</span><span class="pun">(</span><span class="pln">writers</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> compositeItemWriter</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre></div>

        
<p>
          接下来需要配置每一个被委托的 writer；例如 <i>Interaction</i> 和 <i>InteractionMetadata</i> 对应的 writer。
        </p>

        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;bean</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"interactionMetadataWriter"</span><span class="pln">
  </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.batch.MyBatisBatchItemWriter"</span><span class="pln">
  </span><span class="atn">p:sqlSessionTemplate-ref</span><span class="pun">=</span><span class="atv">"batchSessionTemplate"</span><span class="pln">
  </span><span class="atn">p:statementId</span><span class="pun">=</span><span class="atv">"com.my.name.space.batch.InteractionRecordToWriteInMultipleTablesMapper.insertInteractionMetadata"</span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;bean</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"interactionWriter"</span><span class="pln">
  </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"org.mybatis.spring.batch.MyBatisBatchItemWriter"</span><span class="pln">
  </span><span class="atn">p:sqlSessionTemplate-ref</span><span class="pun">=</span><span class="atv">"batchSessionTemplate"</span><span class="pln">
  </span><span class="atn">p:statementId</span><span class="pun">=</span><span class="atv">"com.my.name.space.batch.InteractionRecordToWriteInMultipleTablesMapper.insertInteraction"</span><span class="tag">/&gt;</span></pre></div>

        
<p>和 reader 中的一样，通过 <tt>statementId</tt> 属性指定对应命名空间前缀的查询。</p>

        
<p>而在映射器配置文件中，应该根据每种特定的记录编写特定的语句，如下所示：</p>

        
<div class="source"><pre class="prettyprint"><span class="tag">&lt;insert</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"insertInteractionMetadata"</span><span class="pln">
  </span><span class="atn">parameterType</span><span class="pun">=</span><span class="atv">"com.my.batch.interactions.item.InteractionRecordToWriteInMultipleTables"</span><span class="pln">
  </span><span class="atn">useGeneratedKeys</span><span class="pun">=</span><span class="atv">"true"</span><span class="pln">
  </span><span class="atn">keyProperty</span><span class="pun">=</span><span class="atv">"interaction.interactionMetadata.id"</span><span class="pln">
  </span><span class="atn">keyColumn</span><span class="pun">=</span><span class="atv">"id"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="com">&lt;!-- 此 insert 语句使用了 #{interaction.interactionMetadata.property,jdbcType=...} --&gt;</span><span class="pln">
</span><span class="tag">&lt;/insert&gt;</span><span class="pln">
</span><span class="tag">&lt;insert</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"insertInteraction"</span><span class="pln">
  </span><span class="atn">parameterType</span><span class="pun">=</span><span class="atv">"com.my.batch.interactions.item.InteractionRecordToWriteInMultipleTables"</span><span class="pln">
  </span><span class="atn">useGeneratedKeys</span><span class="pun">=</span><span class="atv">"true"</span><span class="pln">
  </span><span class="atn">keyProperty</span><span class="pun">=</span><span class="atv">"interaction.id"</span><span class="pln">
  </span><span class="atn">keyColumn</span><span class="pun">=</span><span class="atv">"id"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="com">&lt;!--
   此 insert 语句对普通的属性使用的是 #{interaction.property,jdbcType=...}
   而对于 InteractionMetadata 属性使用的是 #{interaction.interactionMetadata.property,jdbcType=...}
  --&gt;</span><span class="pln">
</span><span class="tag">&lt;/insert&gt;</span></pre></div>

        
<p>
          执行的时候会怎么样呢？首先，<tt>insertInteractionMetadata</tt> 将会被调用，update 语句被设置为返回由 JDBC 驱动返回的主键（参考 <tt>keyProperty</tt> 和 <tt>keyColumn</tt>）。由于 <tt>InteractionMetadata</tt> 的对象被此查询更新了，下一个查询将可以通过 <tt>insertInteraction</tt> 开展父对象 <tt>Interaction</tt> 的写入工作。
        </p>

        
<p>
          <b><i>然而要注意，JDBC 驱动在这方面的行为并不总是与此相一致。在编写文档时，H2 的数据库驱动 1.3.168 甚至只在 BATCH 模式下返回最后的索引值（参考 org.h2.jdbc.JdbcStatement#getGeneratedKeys），而 MySQL 的 JDBC 驱动则工作良好并返回所有 ID。
          </i></b>
        </p>
      </div>
    </div>
  

        </div>
      </div>
    </div>
    <hr>
    <footer>
      <div class="container-fluid">
        <div class="row-fluid">
            <p>Copyright ©2010–2020
<a href="http://www.mybatis.org/">MyBatis.org</a>.
All rights reserved.</p>
        </div>
      </div>
    </footer>
  

<div class="xl-chrome-ext-bar" id="xl_chrome_ext_{4DB361DE-01F7-4376-B494-639E489D19ED}" style="display: none;">
      <div class="xl-chrome-ext-bar__logo"></div>

      <a id="xl_chrome_ext_download" href="javascript:;" class="xl-chrome-ext-bar__option">下载视频</a>
      <a id="xl_chrome_ext_close" href="javascript:;" class="xl-chrome-ext-bar__close"></a>
    </div></body></html>